package org.artifact.core.db;

import cn.hutool.core.thread.NamedThreadFactory;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import org.artifact.core.cache.StructCache;

import java.io.File;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Function;

@SuppressWarnings("unchecked")
public abstract class AbstractDao<T extends AbstractEntity<T>> {

    static final Log log = LogFactory.get(AbstractDao.class);
    /** 表名称 */
    protected String tableName;
    // 待入库集合
    protected Map<Object, T> cachedUpdates = new ConcurrentHashMap<>();

    protected Map<Object, T> cachedDeletes = new ConcurrentHashMap<>();

    protected StructCache<Object,T> cache = new StructCache<>();

    // 非守护线程，避免存储数据不完整
    protected ExecutorService saveThreadPool = Executors.newSingleThreadExecutor(new NamedThreadFactory("Save-Thread-" + this.getClass().getSimpleName(), false));

//	{
//		if (!existsTable()) {
//			createTable();
//		}
//	}


    /**
     * 返回列表
     */
    protected List<T> resultList(Callable<List<T>> callable, Function<T, Boolean> fun) {
        // 过滤数据
        Future<List<T>> future = saveThreadPool.submit(() -> {
            List<T> result = callable.call();

            List<T> lists = new ArrayList<>();

            for (T t : cachedUpdates.values()) {
                if (fun.apply(t)) {
                    lists.add(t);
                }
            }

            for (T t : result) {
                if (cachedDeletes.containsKey(t.pk())) {
                    continue;
                }
                if (cachedUpdates.containsKey(t.pk())) {
                    continue;
                }
                lists.add(t);
            }
            return lists;
        });

        try {
            return future.get(3000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            log.error(e);
            return new ArrayList<>();
        }
    }

    /**
     * 返回单个
     */
    protected T resultFirst(Callable<T> callable, Function<T, Boolean> fun) {
        // 过滤数据
        Future<T> future = saveThreadPool.submit(() -> {
            T result = null;
            for (T bean : cachedUpdates.values()) {
                if (fun.apply(bean)) {
                    result = bean;
                    break;
                }
            }
            if (result == null) {
                result = callable.call();
            }

            if (result != null) {
                if (cachedDeletes.containsKey(result.pk())) {
                    return null;
                }
            }

            return result;
        });

        try {
            return future.get(3000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            log.error(e);
            return null;
        }
    }

    /**
     * 单个保存
     */
    public void save(T t) {
        if (isSync()) {
            onSave(t);
        } else {
            final T save = (T) t.toSave();
            saveThreadPool.execute(() -> cachedUpdates.put(t.pk(), save));
        }
        getCache().put(t.pk(),t);
    }

    /**
     * 单个删除
     */
    public void remove(T t) {
        if (isSync()) {
            onRemove(t);
        } else {
            final T save = (T) t.toSave();
            saveThreadPool.execute(() -> cachedDeletes.put(t.pk(), save));
        }
        getCache().remove(t.pk());
    }

    /**
     * 变参保存
     */
    public void save(T... t) {
        if (isSync()) {
            onSave(t);
        } else {
            final Object[] saves = new Object[t.length];
            for (int i = 0; i < t.length; i++) {
                saves[i] = t[i].toSave();
            }
            saveThreadPool.execute(() -> {
                for (Object obj : saves) {
                    T save = (T) obj;
                    cachedUpdates.put(save.pk(), save);
                }
            });
        }
        for (T t1 : t) {
            getCache().put(t1.pk(),t1);
        }
    }

    /**
     * 变参删除
     */
    public void remove(T... t) {
        if (isSync()) {
            onRemove(t);
        } else {
            final Object[] saves = new Object[t.length];
            for (int i = 0; i < t.length; i++) {
                saves[i] = t[i].toSave();
            }
            saveThreadPool.execute(() -> {
                for (Object obj : saves) {
                    T save = (T) obj;
                    cachedDeletes.put(save.pk(), save);
                }
            });
        }
        for (T t1 : t) {
            getCache().remove(t1.pk());
        }
    }

    /**
     * 批量保存
     */
    public void save(Collection<T> coll) {
        if (isSync()) {
            onSave(coll);
        } else {
            final Object[] saves = new Object[coll.size()];
            Iterator<T> iterator = coll.iterator();
            int i = 0;
            while (iterator.hasNext()) {
                saves[i] = iterator.next().toSave();
                i++;
            }
            saveThreadPool.execute(() -> {
                for (Object obj : saves) {
                    T save = (T) obj;
                    cachedUpdates.put(save.pk(), save);
                }
            });
        }
        for (T t1 : coll) {
            getCache().put(t1.pk(),t1);
        }
    }

    /**
     * 批量删除
     */
    public void remove(Collection<T> coll) {
        if (isSync()) {
            onRemove(coll);
        } else {
            final Object[] saves = new Object[coll.size()];
            Iterator<T> iterator = coll.iterator();
            int i = 0;
            while (iterator.hasNext()) {
                saves[i] = iterator.next().toSave();
                i++;
            }
            saveThreadPool.execute(() -> {
                for (Object obj : saves) {
                    T save = (T) obj;
                    cachedDeletes.put(save.pk(), save);
                }
            });
        }
        for (T t1 : coll) {
            getCache().remove(t1.pk());
        }
    }

    /**
     * 停止存储线程
     */
    public void shutdown() {
        // 默认3分钟超时
        int timeout = 180000;
        saveThreadPool.shutdown();
        try {
            if (!saveThreadPool.awaitTermination(timeout, TimeUnit.MILLISECONDS)) {
                log.error("-> SaveThreadPool Shutdown TimeOut Table:{}", this.getClass().getSimpleName());
                saveThreadPool.shutdownNow();
            } else {
                log.info("-> SaveThreadPool Shutdown Success Table:{}", this.getClass().getSimpleName());
            }
        } catch (InterruptedException ie) {
            log.error("-> SaveThreadPool Shutdown Error Table:{} {} : ", this.getClass().getSimpleName(), ie);
            saveThreadPool.shutdownNow();
            Thread.currentThread().interrupt();
        }
        storageDatabase();
    }

    /**
     * 刷入数据库
     */
    public void finishAll() {
        saveThreadPool.execute(() -> {
            storageDatabase();
        });
    }

    private void storageDatabase() {
        try {
            // 1.存储
            if (!cachedUpdates.isEmpty() && onSave(cachedUpdates.values())) {
                cachedUpdates.clear();
            }
            // 2.删除
            if (!cachedDeletes.isEmpty() && onRemove(cachedDeletes.values())) {
                cachedDeletes.clear();
            }
        }catch (Exception e){
            log.error(e);
        }
    }



    /**
     * 备份数据
     */
    public void backUp(File file) {

    }

    /**
     * 恢复数据
     */
    public void restore(File file) {

    }

    /**
     * 缓存实现
     */
    protected StructCache<Object,T> getCache(){
        return cache;
    }

    /**
     * 同步操作
     */
    protected abstract boolean isSync();

    /**
     * 单个保存
     */
    protected abstract boolean onSave(T t);

    /**
     * 单个删除
     */
    protected abstract boolean onRemove(T t);

    /**
     * 变参保存
     */
    protected abstract boolean onSave(T... t);

    /**
     * 变参删除
     */
    protected abstract boolean onRemove(T... t);

    /**
     * 批量保存
     */
    protected abstract boolean onSave(Collection<T> coll);

    /**
     * 批量删除
     */
    protected abstract boolean onRemove(Collection<T> coll);

//    /**
//     * 把待保存的数据-刷入数据库
//     */
//    protected abstract boolean finishSaveCache();
//
//    /**
//     * 把待删除的数据-刷入数据库
//     */
//    protected abstract boolean finishDeleteCache();

    /**
     * 取得数据库名称
     */
    protected abstract String getDBName();

    /**
     * 取得表名称
     */
    public abstract String getTableName();

    /**
     * 取得对应的-EntityClass
     */
    public abstract Class<T> getEntityClass();

    /**
     * 检查表是否存在
     */
    public abstract T createEntity();

    /**
     * 检查表是否存在
     */
    public abstract boolean existsTable();

    /**
     * 创建表
     */
    public abstract boolean createTable();

    /**
     * 查询全部
     */
    public abstract List<T> findAll();

    /**
     * 根据主键查询
     */
    public abstract T findByPrimaryKey(Object primaryKey);

    /**
     * 取得主键字段名称,数据库使用必须是源字段名
     */
    protected abstract String getPKFieldName();
}
